home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / python / test / httpclienttest.py < prev    next >
Encoding:
Python Source  |  2007-11-12  |  69.7 KB  |  1,651 lines

  1. import unittest
  2. import email.Utils
  3. import socket
  4. import tempfile
  5. import traceback
  6. from copy import copy
  7. from StringIO import StringIO
  8.  
  9. from clock import clock
  10. import os
  11.  
  12. from download_utils import cleanFilename
  13. import download_utils
  14. import database
  15. import dialogs
  16. import httpclient
  17. import util
  18. from framework import EventLoopTest, DemocracyTestCase, HadToStopEventLoop
  19.  
  20. class TestingConnectionHandler(httpclient.ConnectionHandler):
  21.     def __init__(self, test):
  22.         super(TestingConnectionHandler, self).__init__()
  23.         self.states['foo'] = self.handleFoo
  24.         self.states['bar'] = self.handleBar
  25.         self.states['noread'] = None
  26.         self.fooData = ''
  27.         self.barData = ''
  28.         self.gotHandleClose = False
  29.         self.closeType = None
  30.         self.test = test
  31.     def handleFoo(self):
  32.         data = self.buffer.read()
  33.         self.fooData += data
  34.         self.test.stopEventLoop(False)
  35.     def handleBar(self):
  36.         data = self.buffer.read()
  37.         self.barData += data
  38.         self.test.stopEventLoop(False)
  39.     def handleClose(self, type):
  40.         self.gotHandleClose = True
  41.         self.closeType = type
  42.         self.test.stopEventLoop(False)
  43.  
  44. class FakeStream:
  45.     def __init__(self, closeCallback=None):
  46.         self.open = False
  47.         self.readCallback = None
  48.         self.closeCallback = closeCallback
  49.         self.timedOut = False
  50.         self.connectionErrback = None
  51.         self.name = ""
  52.         self.output = ''
  53.         self.unprocessed = ''
  54.         self.input = ''
  55.         self.pendingOutput = ''
  56.         self.timedOut = False
  57.         self.pages ={
  58.             'participatoryculture.org':
  59.                {'/democracytest/normalpage.txt':'I AM A NORMAL PAGE\n',
  60.                 '/democracytest/normalpage2.txt':'I AM A NORMAL PAGE\n',
  61.                 '/democracytest/normalpage3.txt':'I AM A NORMAL PAGE\n',
  62.                 '/democracytest/nohead.php':"DYNAMIC CONTENT"},
  63.             'jigsaw.w3.org':
  64.                {'/HTTP/Basic/':"normal page",
  65.                 '/HTTP/Digest/':"normal page"},
  66.             'www.bar.com':
  67.               {'/':"Normal",
  68.                '/2':"Blah"},
  69.             'www.baz.com':
  70.               {'/':"Normal",
  71.                '/2':"Blah"},
  72.             'www.froz.com':
  73.               {'/':"Normal",
  74.                '/2':"Blah"},
  75.             'www.qux.com':
  76.               {'/':"Normal",
  77.                '/2':"Blah"},
  78.             }
  79.  
  80.         # Pages that, like my girlfriend, don't respond to HEAD requests
  81.         self.noheadPages = {
  82.             'participatoryculture.org':
  83.                ['/democracytest/nohead.php']}
  84.         self.basicAuthPages = {
  85.             'jigsaw.w3.org': {'/HTTP/Basic/':'Basic Z3Vlc3Q6Z3Vlc3Q='}
  86.             
  87.          }
  88.         self.digestAuthPages = {
  89.             'jigsaw.w3.org': {'/HTTP/Digest/':'STUFF GOES HERE'}
  90.             
  91.          }
  92.         
  93.  
  94.     def _tryReadCallback(self):
  95.         if len(self.pendingOutput)>0 and self.readCallback:
  96.             response = self.pendingOutput
  97.             self.pendingOutput = ''
  98.             self.readCallback(response)
  99.             
  100.     def _generateResponse(self, method, uri, version, headers):
  101.         text = None
  102.         now = email.Utils.formatdate(usegmt=True)
  103.         if self.pages.has_key(headers["Host"]):
  104.             host_pages = self.pages[headers["Host"]]
  105.             if host_pages.has_key(uri):
  106.                 text = host_pages[uri]
  107.         else:
  108.             self.errback(httpclient.ConnectionError("Can't connect"))
  109.             return None
  110.  
  111.         if text is not None:
  112.             if method == "GET":
  113.                 if ((self.basicAuthPages.has_key(headers["Host"])) and
  114.                     (uri in self.basicAuthPages[headers["Host"]].keys()) and
  115.                     (not headers.has_key('Authorization') or
  116.                      (self.basicAuthPages[headers["Host"]][uri] !=
  117.                             headers['Authorization']))):
  118.                     text = "Not authorized"
  119.                     return"""HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm="test"\r\nContent-Type: text/html; charset=UTF-8\r\nDate: %s\r\nContent-Length: %d\r\n\r\n%s""" % (
  120.                         now, len(text), text)
  121.                 else:
  122.                     if ((self.digestAuthPages.has_key(headers["Host"])) and
  123.                         (uri in self.digestAuthPages[headers["Host"]].keys()) and
  124.                         (not headers.has_key('Authorization') or
  125.                          (self.digestAuthPages[headers["Host"]][uri] !=
  126.                           headers['Authorization']))):
  127.                         text = "Not authorized"
  128.                         return"""HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Digest realm="test",domain="/HTTP/Digest",nonce="13dc6f6b70fec989c0d5bd5956818b33"\r\nContent-Type: text/html; charset=UTF-8\r\nDate: %s\r\nContent-Length: %d\r\n\r\n%s""" % (
  129.                             now, len(text), text)
  130.                     else:
  131.                         return"""HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=UTF-8\r\nLast-Modified: %s\r\nDate: %s\r\nContent-Length: %d\r\n\r\n%s""" % (
  132.                             now, now, len(text), text)
  133.             
  134.             elif method == "HEAD":
  135.                 if ((self.noheadPages.has_key(headers["Host"])) and
  136.                     (uri in self.noheadPages[headers["Host"]])):
  137.                     return"HTTP/1.1 405 NOT ALLOWED\r\nDate: %s\r\n\r\n" % (
  138.                         now)
  139.                 else:
  140.                     return"""HTTP/1.1 200 OK\r\nContent-Type: text/plain; charset=UTF-8\r\nLast-Modified: %s\r\nDate: %s\r\nContent-Length: %d\r\n\r\n""" % (
  141.                     now, now, len(text))
  142.  
  143.         text = "<h1>Not found</h1>"
  144.         return"""HTTP/1.1 404 Not Found\r\nContent-Type: text/html; charset=UTF-8\r\nDate: %s\r\nContent-Length: %d\r\n\r\n%s""" % (
  145.             now, len(text), text)
  146.  
  147.     def _processRequest(self, method, uri, version, headers):
  148.         response = self._generateResponse(method,uri, version, headers)
  149.         if response is not None:
  150.             self.pendingOutput += response
  151.             self._tryReadCallback()
  152.  
  153.     def _processData(self, data):
  154.         self.unprocessed += data
  155.         while self.unprocessed.find("\r\n\r\n") != -1:
  156.             requests = self.unprocessed.split("\r\n\r\n",1)
  157.             self.unprocessed = requests[1]
  158.             headers = requests[0].split("\r\n")
  159.             (request_method, request_uri, request_version) =  \
  160.                              headers.pop(0).split(' ')
  161.             headers = dict([x.split(': ',1) for x in headers])
  162.             self._processRequest(request_method, request_uri,
  163.                                  request_version, headers)
  164.  
  165.  
  166.     def __str__(self):
  167.         if self.name:
  168.             return "%s: %s" % (type(self).__name__, self.name)
  169.         else:
  170.             return "Unknown %s" % (type(self).__name__,)
  171.  
  172.     def startReadTimeout(self):
  173.         pass
  174.  
  175.     def stopReadTimeout(self):
  176.         pass
  177.  
  178.     def openConnection(self, host, port, callback, errback, disabledReadTimeout=None):
  179.         self.name = "Outgoing %s:%s" % (host, port)
  180.         self.output = ''
  181.         self.host = host
  182.         self.port = port
  183.         self.open = True
  184.         self.errback = errback
  185.         self.dsiabledReadTimeout = disabledReadTimeout
  186.         callback(self)
  187.  
  188.     def acceptConnection(self, host, port, callback, errback):
  189.         errback()
  190.  
  191.     def closeConnection(self):
  192.         self.open = False
  193.  
  194.     def isOpen(self):
  195.         return self.open
  196.  
  197.     def sendData(self, data, callback = None):
  198.         if not self.isOpen():
  199.             raise ValueError("Socket not connected")
  200.         self.output += data
  201.         self._processData(data)
  202.  
  203.     def startReading(self, readCallback):
  204.         if not self.isOpen():
  205.             raise ValueError("Socket not connected")
  206.         self.readCallback = readCallback
  207.         self._tryReadCallback()
  208.  
  209.     def stopReading(self):
  210.         """Stop reading from the socket."""
  211.         if not self.isOpen():
  212.             raise ValueError("Socket not connected")
  213.         self.readCallback = None
  214.  
  215.     def onReadTimeout(self):
  216.         raise IOError("Read Timeout")
  217.  
  218.     def handleSocketError(self, code, msg, operation):
  219.         raise IOError("Socket Error")
  220.  
  221. class DumbFakeStream(FakeStream):
  222.     def _generateResponse(self, method, uri, version, headers):
  223.         return None
  224.  
  225. class TestingHTTPConnection(httpclient.HTTPConnection):
  226.     """HTTPConnection that doesn't actually connect to the network."""
  227.     streamFactory = FakeStream
  228.  
  229. class TestingHTTPSConnection(httpclient.HTTPSConnection):
  230.     """HTTPSConnection that doesn't actually connect to the network."""
  231.     streamFactory = FakeStream
  232.  
  233. class DumbTestingHTTPConnection(httpclient.HTTPConnection):
  234.     """HTTPConnection that doesn't actually do much of anything."""
  235.  
  236.     streamFactory = DumbFakeStream
  237.  
  238. class TestingHTTPConnectionPool(httpclient.HTTPConnectionPool):
  239.     MAX_CONNECTIONS = 4 # makes testing more sane
  240.     HTTP_CONN = TestingHTTPConnection
  241.     HTTPS_CONN = TestingHTTPSConnection
  242.  
  243.     def getConnection(self, scheme, host, port=None, type='active'):
  244.         if port is None:
  245.             if scheme == 'https':
  246.                 port = 443
  247.             else:
  248.                 port = 80
  249.         conns = self._getServerConnections(scheme, host, port)
  250.         for conn in conns[type]:
  251.             return conn # returns the 1st one we find
  252.  
  253.     def assertConnectionStarted(self, url):
  254.         assert self.checkConnectionStarted(url)
  255.     def assertConnectionNotStarted(self, url):
  256.         assert not self.checkConnectionStarted(url)
  257.     def checkConnectionStarted(self, url):
  258.         scheme, host, port, path = httpclient.parseURL(url)
  259.         conns = self._getServerConnections(scheme, host, port)
  260.         for conn in conns['active']:
  261.             try:
  262.                 if conn.host == host and conn.port == port:
  263.                     return True
  264.             except:
  265.                 pass
  266.         return False
  267.     def closeConnection(self, scheme, host, port=None, type='active'):
  268.         conn = self.getConnection(scheme, host, port, type)
  269.         conn.handleClose(socket.SHUT_RDWR)
  270.  
  271.  
  272. class DumbTestingHTTPConnectionPool(TestingHTTPConnectionPool):
  273.     HTTP_CONN = DumbTestingHTTPConnection
  274.     HTTPS_CONN = DumbTestingHTTPConnection
  275.     
  276.  
  277. class DumbTestHTTPClient(httpclient.HTTPClient):
  278.     connectionPool = DumbTestingHTTPConnectionPool()
  279.  
  280. class TestHTTPClient(httpclient.HTTPClient):
  281.     connectionPool = TestingHTTPConnectionPool()
  282.  
  283. class TestingHeaderGrabber(httpclient.HTTPHeaderGrabber):
  284.     connectionPool = TestingHTTPConnectionPool()
  285.  
  286. class TestingAuthDelegate:
  287.     def __init__(self):
  288.         self.logins = []
  289.     def addLogin(self, user, password):
  290.         self.logins.append((user, password))
  291.  
  292.     def runDialog(self, dialog):
  293.         if self.logins:
  294.             user, password = self.logins.pop(0)
  295.             dialog.runCallback(dialogs.BUTTON_OK, user, password)
  296.         else:
  297.             dialog.runCallback(None)
  298.  
  299. def startResponse(version='1.1', status=200, headers={}):
  300.     rv = """\
  301. HTTP/%s %s OK\r
  302. Content-Type: text/plain; charset=ISO-8859-1\r
  303. Last-Modified: Wed, 10 May 2006 22:30:33 GMT\r
  304. Date: Wed, 10 May 2006 22:38:39 GMT\r
  305. """ % (version, status)
  306.     for key, value in headers.items():
  307.         rv += '%s: %s\r\n' % (key, value)
  308.     rv += '\r\n'
  309.     return rv
  310.  
  311. class AsyncSocketTest(EventLoopTest):
  312.     def setUp(self):
  313.         self.data = None
  314.         self.errbackCalled = False
  315.         self.callbackCalled = False
  316.         self.fakeCallbackError = False
  317.         EventLoopTest.setUp(self)
  318.  
  319.     def callback(self, data):
  320.         if self.fakeCallbackError:
  321.             1/0
  322.         self.data = data
  323.         self.callbackCalled = True
  324.         self.stopEventLoop(False)
  325.  
  326.     def errback(self, error):
  327.         self.data = error
  328.         self.errbackCalled = True
  329.         self.stopEventLoop(False)
  330.  
  331. class NetworkBufferTest(DemocracyTestCase):
  332.     def setUp(self):
  333.         self.buffer = httpclient.NetworkBuffer()
  334.  
  335. #    def testMemory(self):
  336. #        i = 0
  337. #        data = "c" * 1024
  338. #        buffer = httpclient.NetworkBuffer()
  339. #        while (i < 100000):
  340. #            buffer.addData(data)
  341. #            buffer.read(512)
  342. #            i = i + 1
  343. #
  344.     def testReadLine(self):
  345.         self.buffer.addData("HEL")
  346.         self.assertEquals(self.buffer.readline(), None)
  347.         self.buffer.addData("LO\r\n")
  348.         self.assertEquals(self.buffer.readline(), 'HELLO')
  349.         self.buffer.addData("HOWS\r\nIT\nGOING\r\nCRONLY\rDOESNTBREAK")
  350.         self.assertEquals(self.buffer.readline(), 'HOWS')
  351.         self.assertEquals(self.buffer.readline(), 'IT')
  352.         self.assertEquals(self.buffer.readline(), 'GOING')
  353.         self.assertEquals(self.buffer.readline(), None)
  354.         self.assertEquals(self.buffer.read(), "CRONLY\rDOESNTBREAK")
  355.  
  356.     def testRead(self):
  357.         self.buffer.addData("12345678901234567890")
  358.         self.assertEquals(self.buffer.read(4), "1234")
  359.         self.assertEquals(self.buffer.read(6), "567890")
  360.         self.buffer.addData("CARBOAT")
  361.         self.assertEquals(self.buffer.read(), "1234567890CARBOAT")
  362.  
  363.     def testLength(self):
  364.         self.buffer.addData("ONE\r\nTWO")
  365.         self.assertEquals(self.buffer.length, 8)
  366.         self.buffer.readline()
  367.         self.assertEquals(self.buffer.length, 3)
  368.         self.buffer.read(1)
  369.         self.assertEquals(self.buffer.length, 2)
  370.         self.buffer.unread("AAA")
  371.         self.assertEquals(self.buffer.length, 5)
  372.         self.buffer.addData("MORE")
  373.         self.assertEquals(self.buffer.length, 9)
  374.  
  375.     def testGetValue(self):
  376.         self.buffer.addData("ONE")
  377.         self.buffer.addData("TWO")
  378.         self.buffer.addData("THREE")
  379.         self.assertEquals(self.buffer.getValue(), "ONETWOTHREE")
  380.         # check to make sure the value doesn't change as a result
  381.         self.assertEquals(self.buffer.getValue(), "ONETWOTHREE")
  382.  
  383.  
  384. class WeirdCloseConnectionTest(AsyncSocketTest):
  385.     def testCloseDuringOpenConnection(self):
  386.         # Test opening a connection, then closing the HTTPConnection before it
  387.         # happens.  The openConnection callback shouldn't be called
  388.         #
  389.         # open a socket on localhost and try to connect to that, this should
  390.         # be pretty much instantanious, so we don't need a long timeout to
  391.         # runEventLoop
  392.         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  393.         sock.bind( ('127.0.0.1', 0))
  394.         sock.listen(1)
  395.         host, port = sock.getsockname()
  396.         try:
  397.             conn = httpclient.AsyncSocket()
  398.             conn.openConnection(host, port, self.callback, self.errback)
  399.             conn.closeConnection()
  400.             self.runEventLoop(timeout=1, timeoutNormal=True)
  401.             self.assert_(not self.callbackCalled)
  402.             self.assert_(self.errbackCalled)
  403.         finally:
  404.             sock.close()
  405.  
  406.     def testCloseDurringAcceptConnection(self):
  407.         # Test opening a connection, then closing the HTTPConnection before it
  408.         # happens.  The openConnection callback shouldn't be called
  409.         #
  410.         # open a socket on localhost and try to connect to that, this should
  411.         # be pretty much instantanious, so we don't need a long timeout to
  412.         # runEventLoop
  413.         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  414.         try:
  415.             conn = httpclient.AsyncSocket()
  416.             conn.acceptConnection('127.0.0.1', 0, self.callback, self.errback)
  417.             sock.connect((conn.addr, conn.port))
  418.             conn.closeConnection()
  419.             self.runEventLoop(timeout=1, timeoutNormal=True)
  420.             self.assert_(not self.callbackCalled)
  421.             self.assert_(self.errbackCalled)
  422.         finally:
  423.             sock.close()
  424.  
  425. class ConnectionHandlerTest(EventLoopTest):
  426.     def setUp(self):
  427.         EventLoopTest.setUp(self)
  428.         server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  429.         server.bind( ('127.0.0.1', 0) )
  430.         server.listen(1)
  431.         address = server.getsockname()
  432.  
  433.         self.connectionHandler = TestingConnectionHandler(self)
  434.         def stopEventLoop(conn):
  435.             self.stopEventLoop(False)
  436.         self.connectionHandler.openConnection(address[0], address[1],
  437.                 stopEventLoop, stopEventLoop)
  438.         self.runEventLoop()
  439.         self.remoteSocket, address = server.accept()
  440.         self.remoteSocket.setblocking(False)
  441.  
  442. #     def testSend(self):
  443. #         data = 'abcabc' * 1024  * 64
  444. #         self.connectionHandler.sendData(data)
  445. #         self.received = httpclient.NetworkBuffer()
  446. #         def readData():
  447. #             try:
  448. #                 readIn = self.remoteSocket.recv(1024 * 1024)
  449. #             except:
  450. #                 readIn = ''
  451. #             self.received.addData(readIn)
  452. #             if self.received.length == len(data):
  453. #                 self.stopEventLoop(False)
  454. #             else:
  455. #                 self.addTimeout(0.1, readData, 'test')
  456. #         self.addTimeout(0.1, readData, 'test')
  457. #         self.runEventLoop()
  458. #         self.assert_(self.received.read() == data)
  459.  
  460. #     def testRead(self):
  461. #         self.connectionHandler.changeState('foo')
  462. #         self.remoteSocket.send('abc')
  463. #         self.runEventLoop(timeout=1)
  464. #         self.assertEquals(self.connectionHandler.fooData, 'abc')
  465. #         self.connectionHandler.changeState('bar')
  466. #         self.remoteSocket.send('def')
  467. #         self.runEventLoop(timeout=1)
  468. #         self.assertEquals(self.connectionHandler.barData, 'def')
  469. #         self.remoteSocket.send('ghi')
  470. #         self.connectionHandler.changeState('noread')
  471. #         self.runEventLoop(timeout=0.1, timeoutNormal=True)
  472. #         self.assertEquals(self.connectionHandler.fooData, 'abc')
  473. #         self.assertEquals(self.connectionHandler.barData, 'def')
  474. #         self.connectionHandler.changeState('foo')
  475. #         self.runEventLoop(timeout=1)
  476. #         self.assertEquals(self.connectionHandler.fooData, 'abcghi')
  477.  
  478. #     def testClose(self):
  479. #         self.connectionHandler.closeConnection()
  480. #         self.assert_(not self.connectionHandler.stream.isOpen())
  481. #         # second close shouldn't throw any exceptions
  482. #         self.connectionHandler.closeConnection()
  483.  
  484. #     def testRemoteClose(self):
  485. #         self.connectionHandler.changeState('foo')
  486. #         self.remoteSocket.shutdown(socket.SHUT_WR)
  487. #         self.runEventLoop()
  488. #         self.assertEquals(self.connectionHandler.gotHandleClose, True)
  489.  
  490. #     def testRemoteClose2(self):
  491. #         self.remoteSocket.shutdown(socket.SHUT_RD)
  492. #         self.remoteSocket.close()
  493. #         # NOTE, we have to send enough data so that the OS won't buffer the
  494. #         # entire send call.  Otherwise we may miss that the socket has closed.
  495. #         self.connectionHandler.sendData("A" * 1024 * 1024)
  496. #         self.runEventLoop(timeout=1)
  497. #         self.assertEquals(self.connectionHandler.gotHandleClose, True)
  498.  
  499. #     def testString(self):
  500. #         # just make sure it doesn't throw an exception
  501. #         str(self.connectionHandler)
  502.  
  503. class DumbHTTPClientTest(AsyncSocketTest):
  504.     def setUp(self):
  505.         AsyncSocketTest.setUp(self)
  506.         self.testRequest = DumbTestingHTTPConnection()
  507.         self.testRequest.openConnection('foo.com', 80, lambda x: None,lambda x: None)
  508.         self.testRequest.sendRequest(self.callback, self.errback, "", 80,
  509.                 method='GET', path='/bar/baz;123?a=b') 
  510.         self.authDelegate = TestingAuthDelegate()
  511.         dialogs.setDelegate(self.authDelegate)
  512.  
  513.     def tearDown(self):
  514.         # clear out any HTTPAuth objects in there
  515.         AsyncSocketTest.tearDown(self)
  516.  
  517.     def testScheme(self):
  518.         conn = httpclient.HTTPConnection()
  519.         self.assertEquals(conn.scheme, 'http')
  520.  
  521.     def testRequestLine(self):
  522.         self.assertEquals(self.testRequest.stream.output.split("\r\n")[0],
  523.                 'GET /bar/baz;123?a=b HTTP/1.1')
  524.  
  525.     def testStatusLine(self):
  526.         self.testRequest.handleData("HTTP/1.0 200 OK\r\n")
  527.         self.assertEquals(self.testRequest.version, 'HTTP/1.0')
  528.         self.assertEquals(self.testRequest.status, 200)
  529.         self.assertEquals(self.testRequest.reason, 'OK')
  530.  
  531.     def testStatusLine2(self):
  532.         self.testRequest.handleData("HTTP/1.1 404 Not Found\r\n")
  533.         self.assertEquals(self.testRequest.version, 'HTTP/1.1')
  534.         self.assertEquals(self.testRequest.status, 404)
  535.         self.assertEquals(self.testRequest.reason, 'Not Found')
  536.  
  537.     def testBadStatusLine(self):
  538.         self.testRequest.handleData("HTTP/0.9 200 OK\r\n")
  539.         self.assert_(self.errbackCalled)
  540.         self.assert_(isinstance(self.data, httpclient.BadStatusLine))
  541.  
  542.     def testBadStatusLine2(self):
  543.         self.testRequest.handleData("HTTP/1.0 641 OK\r\n")
  544.         self.assert_(self.errbackCalled)
  545.         self.assert_(isinstance(self.data, httpclient.BadStatusLine))
  546.  
  547.     def testBadStatusLine3(self):
  548.         self.testRequest.handleData("HTTP/1.0 TwoHundred OK\r\n")
  549.         self.assert_(self.errbackCalled)
  550.         self.assert_(isinstance(self.data, httpclient.BadStatusLine))
  551.  
  552.     def testNoReason(self):
  553.         self.testRequest.handleData("HTTP/1.0 200\r\n")
  554.         self.assertEquals(self.testRequest.version, 'HTTP/1.0')
  555.         self.assertEquals(self.testRequest.status, 200)
  556.         self.assertEquals(self.testRequest.reason, '')
  557.  
  558.     def testTryToHandleHTTP0Point9(self):
  559.         self.testRequest.handleData("StartOfThebody\r\n")
  560.         self.assertEquals(self.testRequest.version, 'HTTP/0.9')
  561.         self.assertEquals(self.testRequest.status, 200)
  562.         self.assertEquals(self.testRequest.reason, '')
  563.         self.assertEquals(self.testRequest.buffer.read(),
  564.                 "StartOfThebody\r\n")
  565.  
  566.     fakeResponse = """\
  567. HTTP/1.0 200 OK\r
  568. Content-Type: text/plain; charset=ISO-8859-1\r
  569. Last-Modified: Wed, 10 May 2006 22:30:33 GMT\r
  570. Date: Wed, 10 May 2006 22:38:39 GMT\r
  571. X-Cache: HIT from pcf2.pcf.osuosl.org\r
  572. Server: Apache\r
  573. Content-Length: 14\r
  574. \r
  575. HELLO: WORLD\r\n"""
  576.  
  577.     def testBasicHeaders(self):
  578.         self.testRequest.handleData(self.fakeResponse)
  579.         self.testRequest.handleClose(socket.SHUT_RD)
  580.         headers = self.testRequest.headers
  581.         self.assertEquals(headers['x-cache'], 'HIT from pcf2.pcf.osuosl.org')
  582.         self.assertEquals(headers['server'], 'Apache')
  583.         self.assertEquals(headers['last-modified'], 
  584.             'Wed, 10 May 2006 22:30:33 GMT')
  585.         self.assertEquals(headers['content-length'], '14')
  586.         self.assertEquals(self.testRequest.contentLength, 14)
  587.         self.assertEquals(headers['date'], 'Wed, 10 May 2006 22:38:39 GMT')
  588.         self.assertEquals(headers['content-type'], 
  589.             'text/plain; charset=ISO-8859-1')
  590.  
  591.     def testCallbackError(self):
  592.         self.fakeCallbackError = True
  593.         self.failedCalled = False
  594.         def fakeFailed(*args, **kwargs):
  595.             self.failedCalled = True
  596.         oldFailed = util.failed
  597.         util.failed = fakeFailed
  598.         try:
  599.             self.testRequest.handleData(self.fakeResponse)
  600.             self.testRequest.handleClose(socket.SHUT_RD)
  601.         finally:
  602.             util.failed = oldFailed
  603.         self.assert_(self.failedCalled)
  604.  
  605.     def testHeaderContinuation(self):
  606.         self.testRequest.handleData("HTTP/1.0 200 OK\r\n")
  607.         self.testRequest.handleData("Cont\r\n")
  608.         self.testRequest.handleData(" ent-Type: text/plain\r\n")
  609.         self.assertEquals(self.testRequest.headers['content-type'],
  610.                 'text/plain')
  611.  
  612.     def testHeaderJoin(self):
  613.         self.testRequest.handleData("HTTP/1.0 200 OK\r\n")
  614.         self.testRequest.handleData("x-test-list: 1\r\n")
  615.         self.testRequest.handleData("x-test-list: 2\r\n")
  616.         self.testRequest.handleData("x-test-list: 3\r\n")
  617.         self.testRequest.handleData("x-test-list: 4\r\n")
  618.         self.testRequest.handleData("\r\n")
  619.         self.assertEquals(self.testRequest.headers['x-test-list'], '1,2,3,4')
  620.  
  621.     def testBadHeaderContinuation(self):
  622.         self.testRequest.handleData("HTTP/1.0 200 OK\r\n")
  623.         self.testRequest.handleData("IShouldBeContinued\r\n")
  624.         self.testRequest.handleData("\r\n")
  625.         self.assert_(self.errbackCalled)
  626.         self.assert_(isinstance(self.data, httpclient.BadHeaderLine))
  627.  
  628.     def testWillClose(self):
  629.         self.testRequest.handleData(startResponse(
  630.             headers={'Content-Length': 128}))
  631.         self.assertEquals(self.testRequest.willClose, False)
  632.  
  633.     def testWillClose2(self):
  634.         self.testRequest.handleData(startResponse(
  635.             headers={'Transfer-Encoding':'chunked'}))
  636.         self.assertEquals(self.testRequest.willClose, False)
  637.  
  638.     def testWillClose3(self):
  639.         self.testRequest.handleData(startResponse(version='1.0',
  640.             headers={'Content-Length': 128}))
  641.         # HTTP1.0 connections always close
  642.         self.assertEquals(self.testRequest.willClose, True)
  643.  
  644.     def testWillClose4(self):
  645.         self.testRequest.handleData(startResponse())
  646.         # No content-length and not chunked, we need to close
  647.         self.assertEquals(self.testRequest.willClose, True)
  648.  
  649.     def testWillClose5(self):
  650.         self.testRequest.handleData(startResponse(
  651.                 headers={'Connection': 'close', 'Content-Length': 128}))
  652.         self.assertEquals(self.testRequest.willClose, True)
  653.  
  654.     def testWillClose6(self):
  655.         self.testRequest.handleData(startResponse(
  656.                 headers={'Connection': 'CLoSe', 'Content-Length': 128}))
  657.         self.assertEquals(self.testRequest.willClose, True)
  658.  
  659.     def testPipeline(self):
  660.         self.assertEqual(self.testRequest.pipelinedRequest, None)
  661.         self.testRequest.handleData(startResponse(
  662.             headers={'Content-Length': 128}))
  663.         self.assertEquals(self.testRequest.canSendRequest(), True)
  664.         self.testRequest.sendRequest(self.callback, self.errback, "", 80,
  665.                 path="/pipelined/path")
  666.         self.assertEquals(self.testRequest.pipelinedRequest[6],
  667.                 '/pipelined/path')
  668.         self.testRequest.handleData('a' * 128)
  669.         self.assert_(self.callbackCalled)
  670.         self.assertEquals(self.data['body'], 'a' * 128)
  671.         self.assertEquals(self.testRequest.state, 'response-status')
  672.         self.assertEquals(self.testRequest.path, '/pipelined/path')
  673.         self.assertEquals(self.testRequest.pipelinedRequest, None)
  674.         # make sure that the previous request doesn't mess with the current
  675.         # request
  676.         self.assertEquals(self.testRequest.headers, {})
  677.         self.assertEquals(self.testRequest.body, '')
  678.         self.assertEquals(self.testRequest.status, None)
  679.  
  680.     def testBadPipeline(self):
  681.         self.testRequest.handleData(startResponse())
  682.         # no content length means we can't pipeline a request
  683.         self.assertEquals(self.testRequest.canSendRequest(), False)
  684.         self.assertRaises(httpclient.NetworkError,
  685.             self.testRequest.sendRequest, self.callback, self.errback, "", 80)
  686.  
  687.     def testPipelineNeverStarted(self):
  688.         self.pipelineError = None
  689.         self.testRequest.handleData(startResponse(
  690.             headers={'Content-Length': 128}))
  691.         def pipelineErrback(error):
  692.             self.pipelineError = error
  693.         self.testRequest.sendRequest(self.callback, pipelineErrback, "", 80, 
  694.                 path="/pipelined/path")
  695.         self.testRequest.handleClose(socket.SHUT_RDWR)
  696.         self.assert_(isinstance(self.pipelineError,
  697.             httpclient.PipelinedRequestNeverStarted))
  698.  
  699.     def testPipelineNeverStarted2(self):
  700.         self.pipelineError = None
  701.         self.testRequest.handleData(startResponse(
  702.             headers={'Content-Length': 128}))
  703.         def pipelineErrback(error):
  704.             self.pipelineError = error
  705.         self.testRequest.sendRequest(self.callback, pipelineErrback, "", 80,
  706.                 path="/pipelined/path")
  707.         self.testRequest.closeConnection()
  708.         self.assert_(isinstance(self.pipelineError,
  709.             httpclient.PipelinedRequestNeverStarted))
  710.  
  711.     def testContentLengthHandling(self):
  712.         self.testRequest.handleData(startResponse(
  713.             headers={'Content-Length': '5'}))
  714.         self.testRequest.handleData("12345EXTRASTUFF")
  715.         self.assertEquals(self.testRequest.body, '12345')
  716.  
  717.     def testTransferEncodingTrumpsContentLength(self):
  718.         self.testRequest.handleData(startResponse(
  719.             headers={'Content-Length': '5', 'Transfer-Encoding': 'chunked'}))
  720.         self.assertEquals(self.testRequest.contentLength, None)
  721.  
  722.     def test416ContentLength(self):
  723.         """Test the content length after a 416 status code."""
  724.         self.testRequest.handleData(startResponse(
  725.             status=416, headers={'Content-Range': 'bytes */1234'}))
  726.         self.assertEquals(self.testRequest.contentLength, 1234)
  727.  
  728.     def testNoBody(self):
  729.         self.testRequest.handleData(startResponse(status=204))
  730.         self.assertEquals(self.testRequest.state, 'closed')
  731.         self.assertEquals(self.testRequest.body, '')
  732.  
  733.     def testNoBody2(self):
  734.         self.testRequest.handleData(startResponse(status=123))
  735.         self.assertEquals(self.testRequest.state, 'closed')
  736.         self.assertEquals(self.testRequest.body, '')
  737.  
  738.     def testNoBody3(self):
  739.         self.testRequest.method='HEAD'
  740.         self.testRequest.handleData(startResponse())
  741.         self.assertEquals(self.testRequest.state, 'closed')
  742.         self.assertEquals(self.testRequest.body, '')
  743.  
  744.     def testNoBody4(self):
  745.         self.testRequest.bodyDataCallback = lambda data: 0
  746.         self.testRequest.handleData(startResponse(
  747.             headers={"Content-Length":'0'}))
  748.         self.assertEquals(self.testRequest.state, 'ready')
  749.         self.assertEquals(self.testRequest.body, '')
  750.  
  751.     def testSplitUpMessage(self):
  752.         data = self.fakeResponse
  753.         for cutoff in [3, 6, 10, 4, 100, 52]:
  754.             self.testRequest.handleData(data[:cutoff])
  755.             data = data[cutoff:]
  756.         self.testRequest.handleData(data)
  757.         self.testRequest.handleClose(socket.SHUT_RD)
  758.         self.assertEquals(self.testRequest.version, 'HTTP/1.0')
  759.         self.assertEquals(self.testRequest.status, 200)
  760.         self.assertEquals(self.testRequest.reason, 'OK')
  761.         self.assertEquals(self.testRequest.headers['server'],
  762.                 'Apache')
  763.         self.assertEquals(self.testRequest.body, 'HELLO: WORLD\r\n')
  764.  
  765.     def testOneChunk(self):
  766.         self.testRequest.handleData(self.fakeResponse)
  767.         self.testRequest.handleClose(socket.SHUT_RD)
  768.         self.assertEquals(self.testRequest.version, 'HTTP/1.0')
  769.         self.assertEquals(self.testRequest.status, 200)
  770.         self.assertEquals(self.testRequest.reason, 'OK')
  771.         self.assertEquals(self.testRequest.headers['server'],
  772.                 'Apache')
  773.         self.assertEquals(self.testRequest.body, 'HELLO: WORLD\r\n')
  774.  
  775.     def testBadChunkSize(self):
  776.         self.testRequest.handleData(startResponse(
  777.             headers={'Transfer-Encoding': 'chunked'}))
  778.         self.testRequest.handleData("Fifty\r\n")
  779.         self.assert_(self.errbackCalled)
  780.         self.assert_(isinstance(self.data, httpclient.BadChunkSize))
  781.  
  782.     def testIgnoreChunkExtensions(self):
  783.         self.testRequest.handleData(startResponse(
  784.             headers={'Transfer-Encoding': 'chunked'}))
  785.         self.testRequest.handleData("ff ;ext1=2 ; ext3=4\r\n")
  786.         self.assert_(not self.errbackCalled)
  787.         self.assertEquals(self.testRequest.state, 'chunk-data')
  788.         self.assertEquals(self.testRequest.chunkSize, 255)
  789.  
  790.     def testChunkWithoutCRLF(self):
  791.         self.testRequest.handleData(startResponse(
  792.             headers={'Transfer-Encoding': 'chunked'}))
  793.         self.testRequest.handleData("5\r\n")
  794.         self.testRequest.handleData("12345RN") # "RN" should have been "\r\n"
  795.         self.assert_(self.errback)
  796.         self.assert_(isinstance(self.data, httpclient.CRLFExpected))
  797.  
  798.     def testPrematureClose(self):
  799.         data = self.fakeResponse
  800.         self.testRequest.handleData(data[:123])
  801.         self.testRequest.handleClose(socket.SHUT_RD)
  802.         self.assert_(self.errbackCalled)
  803.         self.assert_(isinstance(self.data, httpclient.ServerClosedConnection))
  804.  
  805. class HTTPClientTestBase(AsyncSocketTest):
  806.     def setUp(self):
  807.         AsyncSocketTest.setUp(self)
  808.         self.testRequest = TestingHTTPConnection()
  809.         self.testRequest.openConnection('foo.com', 80, lambda x: None, lambda x: None)
  810.         self.testRequest.sendRequest(self.callback, self.errback, "", 80, method='GET', path='/bar/baz;123?a=b') 
  811.         self.authDelegate = TestingAuthDelegate()
  812.         dialogs.setDelegate(self.authDelegate)
  813.         TestHTTPClient.connectionPool = TestingHTTPConnectionPool()
  814.         TestingHeaderGrabber.connectionPool = TestingHTTPConnectionPool()
  815.  
  816.     def tearDown(self):
  817.         # clear out any HTTPAuth objects in there
  818.         AsyncSocketTest.tearDown(self)
  819.  
  820.  
  821. class HTTPClientTest(HTTPClientTestBase):
  822.     def testRealRequest(self):
  823.         url = 'http://participatoryculture.org/democracytest/normalpage.txt'
  824.         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient)
  825.         self.runEventLoop(timeout=10)
  826.         self.assert_(self.callbackCalled)
  827.         self.assertEquals(self.data['body'], "I AM A NORMAL PAGE\n")
  828.  
  829.     def testGrabHeaders(self):
  830.         url = 'http://participatoryculture.org/democracytest/normalpage.txt'
  831.         httpclient.grabHeaders(url, self.callback, self.errback, TestingHeaderGrabber)
  832.         self.runEventLoop(timeout=10)
  833.         self.assert_(self.callbackCalled)
  834.         self.assertEquals(self.data['body'], "")
  835.         self.assertEquals(self.data['status'], 200)
  836.         self.assertEquals(self.data['original-url'], self.data['updated-url'])
  837.         self.assertEquals(self.data['original-url'], self.data['redirected-url'])
  838.  
  839.     def testGrabHeaders2(self):
  840.         url = 'http://participatoryculture.org/democracytest/nohead.php'
  841.         httpclient.grabHeaders(url, self.callback, self.errback,
  842.                                clientClass = TestingHeaderGrabber)
  843.         self.runEventLoop(timeout=10)
  844.         self.assert_(self.callbackCalled)
  845.         self.assertEquals(self.data['body'], "")
  846.         self.assertEquals(self.data['status'], 200)
  847.         self.assertEquals(self.data['original-url'], self.data['updated-url'])
  848.         self.assertEquals(self.data['original-url'], self.data['redirected-url'])
  849.  
  850.     def testGrabHeadersCancel(self):
  851.         url = 'http://participatoryculture.org/democracytest/normalpage.txt'
  852.         client = httpclient.grabHeaders(url, self.callback, self.errback,
  853.                                         clientClass = TestingHeaderGrabber)
  854.         client.cancel()
  855.  
  856.         # Hmmmm.... it looks like the behavior changed, so cancel no
  857.         # longer triggers an errback. I guess this is okay. --NN
  858.         self.assertRaises(HadToStopEventLoop, lambda:self.runEventLoop(timeout=1))
  859.         #self.assert_(self.errbackCalled)
  860.  
  861.     def testConnectionFailure(self):
  862.         httpclient.grabURL("http://slashdot.org:123123", self.callback, 
  863.                 self.errback, clientClass=TestHTTPClient)
  864.         self.runEventLoop()
  865.         self.assert_(self.errbackCalled)
  866.         self.assertEquals(self.data.__class__, httpclient.ConnectionError)
  867.  
  868.     def testMultipleRequests(self):
  869.         def middleCallback(data):
  870.             self.firstData = data
  871.             req.sendRequest(self.callback, self.errback, "participatoryculture.org", 80, method='GET', path='/democracytest/normalpage.txt')
  872.  
  873.         req = TestingHTTPConnection()
  874.         def stopEventLoop(conn):
  875.             self.stopEventLoop(False)
  876.         self.addIdle(lambda: req.openConnection('participatoryculture.org', 80, stopEventLoop, self.errback), "Open connection")
  877.         self.runEventLoop()
  878.         self.assert_(not self.errbackCalled)
  879.         self.addIdle(lambda: req.sendRequest(middleCallback,
  880.                                              self.errback,
  881.                                              "participatoryculture.org",
  882.                                              80,
  883.                                              method='GET',
  884.                                              path='/democracytest/normalpage.txt'),
  885.                      "Send Request")
  886.         self.runEventLoop()
  887.         self.assert_(not self.errbackCalled)
  888.         self.assertEquals(self.firstData['body'], self.data['body'])
  889.  
  890.     def testUnexpectedStatusCode(self):
  891.         """Test what happens when we get a bad status code.  
  892.         
  893.         The header callback should be called, but the on body data callback
  894.         shouldn't.  Also, we should call the errback instead of the callback.
  895.         """
  896.         self.onHeadersCalled = self.onBodyDataCalled = False
  897.         def onHeaders(headers):
  898.             self.onHeadersCalled = True
  899.         def onBodyData(data):
  900.             self.onBodyDataCalled = True
  901.         url = "http://participatoryculture.org/404"
  902.         client = TestHTTPClient(url, self.callback, self.errback,
  903.                                 onHeaders, onBodyData)
  904.         client.startRequest()
  905.         self.runEventLoop()
  906.         self.assert_(self.onHeadersCalled)
  907.         self.assert_(not self.onBodyDataCalled)
  908.         self.assert_(not self.callbackCalled)
  909.         self.assert_(self.errbackCalled)
  910.  
  911.     def testHeaderCallback(self):
  912.         def headerCallback(response):
  913.             self.headerResponse = copy(response)
  914.             self.callbackCalledInHeaderCallback = self.callbackCalled
  915.             self.errbackCalledInHeaderCallback = self.errbackCalled
  916.             self.stopEventLoop(False)
  917.         url = 'http://participatoryculture.org/democracytest/normalpage.txt'
  918.         httpclient.grabURL(url, self.callback, self.errback,
  919.                 headerCallback=headerCallback, clientClass=TestHTTPClient)
  920.         self.runEventLoop()
  921.         self.assert_(not self.callbackCalledInHeaderCallback)
  922.         self.assert_(not self.errbackCalledInHeaderCallback)
  923.         self.assert_(self.headerResponse['content-type'].startswith('text/plain'))
  924.         self.assertEquals(self.headerResponse['body'], None)
  925.  
  926.     def testHeaderCallbackCancel(self):
  927.         def headerCallback(response):
  928.             reqId.cancel()
  929.             self.stopEventLoop(False)
  930.         url = 'http://participatoryculture.org/democracytest/normalpage.txt'
  931.         reqId = httpclient.grabURL(url, self.callback, self.errback,
  932.                 headerCallback=headerCallback, clientClass=TestHTTPClient)
  933.         self.failedCalled = False
  934.         def fakeFailed(*args, **kwargs):
  935.             self.failedCalled = True
  936.         oldFailed = util.failed
  937.         util.failed = fakeFailed
  938.         self.runEventLoop()
  939.         util.failed = oldFailed
  940.         self.assert_(not self.callbackCalled)
  941.         self.assert_(not self.errbackCalled)
  942.         self.assert_(not self.failedCalled)
  943.  
  944.     def testBodyDataCallbackCancel(self):
  945.         def bodyDataCallback(response):
  946.             reqId.cancel()
  947.             self.stopEventLoop(False)
  948.         url = 'http://participatoryculture.org/democracytest/normalpage.txt'
  949.         reqId = httpclient.grabURL(url, self.callback, self.errback,
  950.                 bodyDataCallback=bodyDataCallback, clientClass=TestHTTPClient)
  951.         self.failedCalled = False
  952.         def fakeFailed(*args, **kwargs):
  953.             self.failedCalled = True
  954.         oldFailed = util.failed
  955.         util.failed = fakeFailed
  956.         self.runEventLoop()
  957.         util.failed = oldFailed
  958.         self.assert_(not self.callbackCalled)
  959.         self.assert_(not self.errbackCalled)
  960.         self.assert_(not self.failedCalled)
  961.  
  962.     def testBodyDataCallback(self):
  963.         self.lastSeen = None
  964.         def bodyDataCallback(data):
  965.             self.lastSeen = data
  966.         self.testRequest.bodyDataCallback = bodyDataCallback
  967.         self.testRequest.handleData(startResponse(
  968.                     headers={'content-length':'20'}))
  969.         self.assertEquals(self.lastSeen, None)
  970.         self.testRequest.handleData("12345")
  971.         self.assertEquals(self.lastSeen, "12345")
  972.         self.testRequest.handleData("1234567890")
  973.         self.assertEquals(self.lastSeen, "1234567890")
  974.         self.testRequest.handleData("1234567890")
  975.         self.assertEquals(self.lastSeen, "12345")
  976.  
  977.     def testBodyDataCallbackChunked(self):
  978.         self.lastSeen = None
  979.         def bodyDataCallback(data):
  980.             self.lastSeen = data
  981.         self.testRequest.bodyDataCallback = bodyDataCallback
  982.         self.testRequest.handleData(startResponse(
  983.             headers={'transfer-encoding': 'chunked'}))
  984.         self.testRequest.handleData("5\r\nHI")
  985.         self.assertEquals(self.lastSeen, "HI")
  986.         self.testRequest.handleData("BEN\r\n")
  987.         self.assertEquals(self.lastSeen, "BEN")
  988.         self.testRequest.handleData("A\r\n")
  989.         self.assertEquals(self.lastSeen, "BEN")
  990.         self.testRequest.handleData("1234567890\r\n0")
  991.         self.assertEquals(self.lastSeen, "1234567890")
  992.         self.assert_(not self.callbackCalled)
  993.         self.testRequest.handleData("\r\n\r\n")
  994.         self.assert_(self.callbackCalled)
  995.  
  996.     def testBodyDataCallbackRealRequest(self):
  997.         url = 'http://participatoryculture.org/democracytest/normalpage.txt'
  998.         self.gotData = ''
  999.         def bodyDataCallback(data):
  1000.             self.gotData += data
  1001.             if self.gotData == 'I AM A NORMAL PAGE\n':
  1002.                 self.stopEventLoop(False)
  1003.  
  1004.         httpclient.grabURL(url, self.callback, self.errback,
  1005.                 bodyDataCallback=bodyDataCallback, clientClass=TestHTTPClient)
  1006.         self.runEventLoop()
  1007.         self.assertEquals(self.gotData, 'I AM A NORMAL PAGE\n')
  1008.  
  1009.     def testAuth(self):
  1010.         self.authDelegate.addLogin(u'ben', u'baddpassword')
  1011.         self.authDelegate.addLogin(u'guest', u'guest')
  1012.         url = 'http://jigsaw.w3.org/HTTP/Basic/'
  1013.         client = TestHTTPClient(url, self.callback, self.errback)
  1014.         client.startRequest()
  1015.         self.runEventLoop()
  1016.         self.assert_(self.callbackCalled)
  1017.         self.assertEquals(self.data['status'], 200)
  1018.         self.assertEquals(client.authAttempts, 2)
  1019.  
  1020.     def testBadAuth(self):
  1021.         self.authDelegate.addLogin(u'baduser', u'baddpass')
  1022.         self.authDelegate.addLogin(u'anotherbadtry', u'god')
  1023.         self.authDelegate.addLogin(u'billgates', u'password')
  1024.         url = 'http://jigsaw.w3.org/HTTP/Basic/'
  1025.         client = TestHTTPClient(url, self.callback, self.errback)
  1026.         client.startRequest()
  1027.         self.runEventLoop()
  1028.         self.assert_(self.errbackCalled)
  1029.         self.assert_(isinstance(self.data, httpclient.AuthorizationFailed))
  1030.         self.assertEquals(client.authAttempts, 3)
  1031.  
  1032.     def testDigestAuth(self):
  1033.         # we don't support digest authorization yet, make sure we get the
  1034.         # right errback at least
  1035.         url = 'http://jigsaw.w3.org/HTTP/Digest/'
  1036.         client = TestHTTPClient(url, self.callback, self.errback)
  1037.         client.startRequest()
  1038.         self.runEventLoop()
  1039.         self.assert_(self.errbackCalled)
  1040.         self.assert_(isinstance(self.data, httpclient.AuthorizationFailed))
  1041.         self.assertEquals(client.authAttempts, 0)
  1042.  
  1043.  
  1044.  
  1045. #     def testChunkedData(self):
  1046. #         url = 'http://jigsaw.w3.org/HTTP/ChunkedScript'
  1047. #         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient)
  1048. #         self.runEventLoop(timeout=5)
  1049. #         header = """\
  1050. # This output will be chunked encoded by the server, if your client is HTTP/1.1
  1051. # Below this line, is 1000 repeated lines of 0-9.
  1052. # -------------------------------------------------------------------------"""
  1053. #         bodyLine = """\
  1054. # 01234567890123456789012345678901234567890123456789012345678901234567890"""
  1055. #         lines = self.data['body'].split('\n')
  1056. #         headerLines = header.split('\n')
  1057. #         self.assertEquals(lines[0], headerLines[0])
  1058. #         self.assertEquals(lines[1], headerLines[1])
  1059. #         self.assertEquals(lines[2], headerLines[2])
  1060. #         for x in range(3, 1003):
  1061. #             self.assertEquals(lines[x], bodyLine)
  1062.  
  1063. #     def testCookie(self):
  1064. #         url = 'http://participatoryculture.org/democracytest/cookie.php'
  1065. #         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient)
  1066. #         self.runEventLoop(timeout=5)
  1067. #         self.assertEquals(len(self.data['cookies']),1)
  1068. #         self.assert_(self.data['cookies'].has_key('DemocracyTestCookie'))
  1069. #         self.assertEquals(self.data['cookies']['DemocracyTestCookie']['Value'], 'foobar')
  1070. #         httpclient.grabURL(url, self.callback, self.errback,cookies = self.data['cookies'], clientClass=TestHTTPClient)
  1071. #         self.runEventLoop(timeout=2)
  1072. #         self.assertNotEqual(self.data['body'].find('DemocracyTestCookie:foobar'),-1)
  1073.  
  1074.     def testParseURL(self):
  1075.         (scheme, host, port, path) = \
  1076.                 httpclient.parseURL("https://www.foo.com/abc;123?a=b#4")
  1077.         self.assertEquals(scheme, 'https')
  1078.         self.assertEquals(host, 'www.foo.com')
  1079.         self.assertEquals(port, 443)
  1080.         self.assertEquals(path, '/abc;123?a=b')
  1081.         (scheme, host, port, path) = \
  1082.                 httpclient.parseURL("http://www.foo.com/abc;123?a=b#4")
  1083.         self.assertEquals(port, 80)
  1084.         (scheme, host, port, path) = \
  1085.                 httpclient.parseURL("http://www.foo.com:5000/abc;123?a=b#4")
  1086.         self.assertEquals(port, 5000)
  1087.         # I guess some feeds have bad url, with a double port in them, test
  1088.         # that we handle these.
  1089.         (scheme, host, port, path) = \
  1090.                 httpclient.parseURL("http://www.foo.com:123:123/abc;123?a=b#4")
  1091.         self.assertEquals(port, 123)
  1092.  
  1093. #     def checkRedirect(self, url, redirectUrl, updatedUrl, **extra):
  1094. #         self.errbackCalled = self.callbackCalled = False
  1095. #         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient, **extra)
  1096. #         self.runEventLoop(timeout=20)
  1097. #         self.assert_(not self.errbackCalled)
  1098. #         self.assert_(self.callbackCalled)
  1099. #         self.assertEquals(self.data['redirected-url'], redirectUrl)
  1100. #         self.assertEquals(self.data['updated-url'], updatedUrl)
  1101.  
  1102. #     def test307Redirect(self):
  1103. #         self.checkRedirect('http://jigsaw.w3.org/HTTP/300/307.html',
  1104. #                 'http://jigsaw.w3.org/HTTP/300/Overview.html',
  1105. #                 'http://jigsaw.w3.org/HTTP/300/307.html')
  1106.  
  1107. #     def test301Redirect(self):
  1108. #         self.checkRedirect('http://jigsaw.w3.org/HTTP/300/301.html',
  1109. #                 'http://jigsaw.w3.org/HTTP/300/Overview.html',
  1110. #                 'http://jigsaw.w3.org/HTTP/300/Overview.html')
  1111.  
  1112. #     def test302Redirect(self):
  1113. #         self.checkRedirect('http://jigsaw.w3.org/HTTP/300/302.html',
  1114. #                 'http://jigsaw.w3.org/HTTP/300/Overview.html',
  1115. #                 'http://jigsaw.w3.org/HTTP/300/302.html')
  1116.  
  1117. #     def test303Redirect(self):
  1118. #         self.checkRedirect('http://jigsaw.w3.org/HTTP/300/Go_303',
  1119. #                 'http://jigsaw.w3.org/HTTP/300/303_ok.html',
  1120. #                 'http://jigsaw.w3.org/HTTP/300/Go_303', method="POST", postVariables={})
  1121. #         self.assertEquals(self.data['method'], 'GET')
  1122.  
  1123. #     def test303RedirectWithData(self):
  1124. #         self.checkRedirect('http://jigsaw.w3.org/HTTP/300/Go_303',
  1125. #                 'http://jigsaw.w3.org/HTTP/300/303_ok.html',
  1126. #                 'http://jigsaw.w3.org/HTTP/300/Go_303', method="POST", postVariables={'foo':'bar'})
  1127. #         self.assertEquals(self.data['method'], 'GET')
  1128.  
  1129. #     def testMultipleRedirect(self):
  1130. #         # The redirect chain is:
  1131. #         # redirect.php PERMAMENT REDIRECT -> redirect2.php
  1132. #         # redirect2.php PERMAMENT REDIRECT -> redirect3.php
  1133. #         # redirect3.php TEMORARY REDIRECT -> end.txt
  1134. #         # The updated-url should be redirect3.php, since that it was the 1st
  1135. #         # redirect that was temporary
  1136. #         self.checkRedirect(
  1137. #                 'http://participatoryculture.org/democracytest/redirect.php',
  1138. #                 'http://participatoryculture.org/democracytest/end.txt',
  1139. #                 'http://participatoryculture.org/democracytest/redirect3.php')
  1140.  
  1141. #     def testFileUpload(self):
  1142. #         self.errbackCalled = self.callbackCalled = False
  1143. #         httpclient.grabURL('http://participatoryculture.org/democracytest/fileupload.php', self.callback, self.errback, method="POST", postFiles = {'myfile': {
  1144. #             'filename' : 'tempfile.txt',
  1145. #             'mimetype' : 'application/octet-stream',
  1146. #             'handle'   : StringIO('This is a test file', clientClass=TestHTTPClient)
  1147. #             }})
  1148. #         self.runEventLoop(timeout=20)
  1149. #         self.assert_(not self.errbackCalled)
  1150. #         self.assert_(self.callbackCalled)
  1151. #         result = self.data['body'].split()
  1152. #         self.assertEqual(result[0], 'tempfile.txt')
  1153. #         self.assertEqual(result[1], 'application/octet-stream')
  1154. #         self.assertEqual(result[2], '0b26e313ed4a7ca6904b0e9369e5b957')        
  1155.  
  1156. #     def testRedirectLimit(self):
  1157. #         url = 'http://participatoryculture.org/democracytest/redirect.php'
  1158. #         client = httpclient.HTTPClient(url, self.callback, self.errback) 
  1159. #         client.MAX_REDIRECTS = 2
  1160. #         client.startRequest()
  1161. #         self.runEventLoop()
  1162. #         self.assert_(self.errbackCalled)
  1163. #         self.assert_(isinstance(self.data, httpclient.UnexpectedStatusCode))
  1164.  
  1165.     def testCleanFilename(self):
  1166.         tempdir = tempfile.gettempdir()
  1167.         def testIt(filename):
  1168.             cleaned = cleanFilename(filename)
  1169.             self.assertEqual(cleaned.__class__, str)
  1170.             self.assertNotEqual(cleaned, '')
  1171.             path = os.path.join(tempdir, cleaned)
  1172.             f = open(path, 'w')
  1173.             f.write("AOEUOAEU")
  1174.             f.close()
  1175.             os.remove(path)
  1176.         testIt(u'iamnormal.txt')
  1177.         testIt(u'???')
  1178.         testIt(u'\xf8benben.jpg')
  1179.         testIt(u'\xf8???.\xf2\xf3x')
  1180.  
  1181.     def testGetFilenameFromResponse(self):
  1182.         client = TestHTTPClient('http://www.foo.com', self.callback, self.errback)
  1183.  
  1184.         def getIt(path, cd=None):
  1185.             response = {'path': path}
  1186.             if cd:
  1187.                 response['content-disposition'] = cd
  1188.             return client.getFilenameFromResponse(response)
  1189.  
  1190.         self.assertEquals("unknown", getIt("/"))
  1191.         self.assertEquals("index.html", getIt("/index.html"))
  1192.         self.assertEquals("index.html", getIt("/path/path2/index.html"))
  1193.         self.assertEquals("unknown", getIt("/path/path2/"))
  1194.         self.assertEquals("myfile.txt", getIt("/", 'filename="myfile.txt"'))
  1195.         self.assertEquals("myfile.txt",
  1196.                           getIt("/", 'filename="myfile.txt"; size=45'))
  1197.         self.assertEquals("myfile.txt",
  1198.                           getIt("/", ' filename =  "myfile.txt"'))
  1199.         self.assertEquals("myfile.txt", getIt("/", 'filename=myfile.txt'))
  1200.         self.assertEquals("myfile.txt",
  1201.                           getIt("/index.html", 'filename="myfile.txt"'))
  1202.         self.assertEquals("lots.of.extensions",
  1203.                           getIt("/", 'filename="lots.of.extensions"'))
  1204.  
  1205.         # FIXME - these two fail
  1206.         self.assertEquals("uncleanfilename", 
  1207.                           getIt("/index", 'filename="\\un/cl:ean*fi?lena<m>|e"'))
  1208.         self.assertEquals("uncleanfil-ename2", 
  1209.                           getIt('/uncl*ean"fil?"ena|m""e2"'))
  1210.  
  1211.     def testGetCharsetFromResponse(self):
  1212.         client = TestHTTPClient('http://participatoryculture.org/democracytest/normal.txt', self.callback,
  1213.                 self.errback)
  1214.         def getIt(contentType):
  1215.             if contentType:
  1216.                 response = {'content-type': contentType}
  1217.             else:
  1218.                 response = {}
  1219.             return client.getCharsetFromResponse(response)
  1220.         self.assertEquals('iso-8859-1', getIt(None))
  1221.         self.assertEquals('iso-8859-1', getIt('gabaldigook'))
  1222.         self.assertEquals('iso-8859-1', getIt("text/html"))
  1223.         self.assertEquals('utf-8', getIt("text/html; charset=utf-8"))
  1224.         self.assertEquals('utf-8', getIt("text/html; charset = utf-8"))
  1225.         self.assertEquals('utf-8', 
  1226.                 getIt("text/html; charset=utf-8; extraparam=2"))
  1227.  
  1228.  
  1229. class HTTPConnectionPoolTest(EventLoopTest):
  1230.     def setUp(self):
  1231.         self.pool = TestingHTTPConnectionPool()
  1232.         super(HTTPConnectionPoolTest, self).setUp()
  1233.  
  1234.     def addRequest(self, url):
  1235.         return self.pool.addRequest(
  1236.             lambda blah: self.addIdle(lambda :self.stopEventLoop(False),
  1237.                                       "Closing connection when request is done"),
  1238.             lambda error: 0, None, None, None, url, "GET", {})
  1239.  
  1240.     
  1241.     def checkCounts(self, activeCount, freeCount, pendingCount):
  1242.         self.assertEquals(self.pool.activeConnectionCount, activeCount)
  1243.         self.assertEquals(self.pool.freeConnectionCount, freeCount)
  1244.         realFreeCount = realActiveCount = 0
  1245.         for key, conns in self.pool.connections.items():
  1246.             realFreeCount += len(conns['free'])
  1247.             realActiveCount += len(conns['active'])
  1248.         self.assertEquals(realActiveCount, activeCount)
  1249.         self.assertEquals(realFreeCount, freeCount)
  1250.         self.assertEquals(pendingCount, len(self.pool.pendingRequests))
  1251.  
  1252.     def testNormalUsage(self):
  1253.         self.addRequest("http://www.foo.com/")
  1254.         self.addRequest("http://www.bar.com/")
  1255.         self.addRequest("http://www.foo.com/2")
  1256.         self.addRequest("http://www.google.com/")
  1257.         self.checkCounts(4, 0, 0)
  1258.  
  1259.     def testOpenConnectionFailed(self):
  1260.         self.pool = TestingHTTPConnectionPool()
  1261.         def stopEventLoop(error):
  1262.             self.stopEventLoop(False)
  1263.         self.pool.addRequest(stopEventLoop, stopEventLoop,
  1264.                 None, None, None, "http://uselessurl/", "GET", {})
  1265.         self.runEventLoop()
  1266.         self.checkCounts(0, 0, 0)
  1267.  
  1268.     def testCounts(self):
  1269.         self.addRequest("http://participatoryculture.org/")
  1270.         self.addRequest("http://participatoryculture.org/democracytest/normalpage.txt")
  1271.         self.addRequest("https://participatoryculture.org/")
  1272.         self.checkCounts(3, 0, 0)
  1273.         self.runEventLoop()
  1274.         self.checkCounts(2, 1, 0)
  1275.         self.pool.closeConnection('https', 'participatoryculture.org')
  1276.         self.checkCounts(1, 1, 0)
  1277.         self.pool.closeConnection('http', 'participatoryculture.org', type='free')
  1278.         self.checkCounts(1, 0, 0)
  1279.  
  1280.     def testServerLimit(self):
  1281.         self.addRequest("http://participatoryculture.org/democracytest/normalpage.txt")
  1282.         self.addRequest("http://participatoryculture.org/democracytest/normalpage2.txt")
  1283.         self.addRequest("https://participatoryculture.org/democracytest/normalpage.txt")
  1284.         self.checkCounts(3, 0, 0)
  1285.         self.addRequest("http://participatoryculture.org/democracytest/normalpage3.txt")
  1286.         self.checkCounts(3, 0, 1)
  1287.         self.pool.assertConnectionNotStarted('http://participatoryculture.org/democracytest/normalpage3.txt')
  1288.         self.runEventLoop()
  1289.         self.checkCounts(3, 0, 0)
  1290.         self.runEventLoop()
  1291.         self.pool.assertConnectionStarted('http://participatoryculture.org/democracytest/normalpage3.txt')
  1292.         self.checkCounts(2, 1, 0)
  1293.  
  1294.     def testTotalLimit(self):
  1295.         self.addRequest("http://participatoryculture.org/democracytest/normalpage.txt")
  1296.         self.addRequest("http://participatoryculture.org/democracytest/normalpage2.txt")
  1297.         self.addRequest("http://www.bar.com/")
  1298.         self.addRequest("http://www.bar.com/2")
  1299.         self.addRequest("http://www.baz.com/")
  1300.         self.addRequest("http://www.froz.com/")
  1301.         self.checkCounts(4, 0, 2)
  1302.  
  1303.     def testBothLimits(self):
  1304.         # This test is kind of pointless now, but I can't see a way to
  1305.         # test it as well as before.
  1306.         self.addRequest("http://participatoryculture.org/")
  1307.         self.addRequest("http://participatoryculture.org/2")
  1308.         self.addRequest("http://participatoryculture.org/3")
  1309.         self.checkCounts(2, 0, 1)
  1310.         self.pool.assertConnectionNotStarted('http://participatoryculture.org/3')
  1311.         self.addRequest("https://www.bar.com/")
  1312.         self.addRequest("http://www.bar.com/2")
  1313.         self.addRequest("http://www.baz.com/")
  1314.         self.checkCounts(4, 0, 2)
  1315.         self.pool.assertConnectionNotStarted('http://www.baz.com/')
  1316.         self.runEventLoop()
  1317.         self.checkCounts(4, 0, 1)
  1318.         self.pool.assertConnectionNotStarted('http://www.baz.com/')
  1319.         self.runEventLoop()
  1320.         self.checkCounts(4, 0, 0)
  1321.         self.runEventLoop()
  1322.  
  1323.     def testDropTheLRU(self):
  1324.         # Check that the first connection dropped when we run out of
  1325.         # connections is the least recently used
  1326.         self.addRequest("http://www.baz.com/")
  1327.         self.addRequest("http://participatoryculture.org/")
  1328.         self.addRequest("http://www.bar.com/")
  1329.         self.addRequest("http://www.froz.com/")
  1330.         self.addRequest("http://www.qux.com/")
  1331.         self.assert_(len(self.pool.connections['http:www.baz.com:80']['free'])==0 and len(self.pool.connections['http:www.baz.com:80']['active'])>0)
  1332.         self.runEventLoop()
  1333.         self.assert_(len(self.pool.connections['http:www.baz.com:80']['free'])==0 and len(self.pool.connections['http:www.baz.com:80']['active'])==0)
  1334.  
  1335.         # 
  1336.  
  1337. #     def testCleanup(self):
  1338.  
  1339.         # I'm not sure what this is testing exactly, so I'm not fixing it
  1340.         
  1341. #         self.addRequest("http://www.foo.com/")
  1342. #         self.addRequest("http://www.bar.com/")
  1343. #         self.addRequest("http://www.baz.com/")
  1344. #         self.addRequest("http://www.qux.com/")
  1345. #         self.pool.finishConnection('http', 'www.foo.com')
  1346. #         self.pool.finishConnection('http', 'www.bar.com')
  1347. #         self.pool.finishConnection('http', 'www.baz.com')
  1348. #         foo = self.pool.getConnection('http', 'www.foo.com', type='free')
  1349. #         bar = self.pool.getConnection('http', 'www.bar.com', type='free')
  1350. #         baz = self.pool.getConnection('http', 'www.baz.com', type='free')
  1351. #         qux = self.pool.getConnection('http', 'www.qux.com', type='active')
  1352. #         now = clock()
  1353. #         foo.idleSince = now-301
  1354. #         bar.idleSince = now-299
  1355. #         self.pool.cleanupPool()
  1356. #         # foo timeout out, bar and baz didn't time out, qux is active, so it
  1357. #         # shouldn't be dropped
  1358. #         self.assert_(not foo.stream.isOpen())
  1359. #         self.assert_(bar.stream.isOpen())
  1360. #         self.assert_(baz.stream.isOpen())
  1361. #         self.assert_(qux.stream.isOpen())
  1362. #         self.assert_('http:www.foo.com:80' not in self.pool.connections)
  1363. #         self.assert_('http:www.bar.com:80' in self.pool.connections)
  1364. #         self.assert_('http:www.baz.com:80' in self.pool.connections)
  1365. #         self.assert_('http:www.qux.com:80' in self.pool.connections)
  1366. #         qux.handleData(startResponse(headers={'Content-Length': 128}))
  1367. #         # qux is now a free connection, but its idleSince is None
  1368. #         self.pool.cleanupPool()
  1369. #         self.assert_('http:www.qux.com:80' in self.pool.connections)
  1370.  
  1371. # class HTTPSConnectionTest(HTTPClientTestBase):
  1372. #     # We should have more tests here, but I have no idea how to fake SSL
  1373. #     # connections.  So I just put some attemps to connect to an https site
  1374. #     # The first https site I found was:
  1375. #     # WAVE - Web Automated Verification of Enrollment
  1376. #     # https://www.gibill.va.gov/wave/
  1377.  
  1378. #     def testScheme(self):
  1379. #         conn = httpclient.HTTPSConnection()
  1380. #         self.assertEquals(conn.scheme, 'https')
  1381.  
  1382. #     def testHTTPSConnection(self):
  1383. #         conn = httpclient.HTTPSConnection()
  1384. #         def handleOpen(data):
  1385. #             conn.sendRequest(self.callback, self.errback, 
  1386. #                     method="GET", path='/wave/')
  1387. #         def handleError(error):
  1388. #             self.stopEventLoop(False)
  1389. #         conn.openConnection("www.gibill.va.gov", 443, handleOpen, handleError)
  1390. #         self.runEventLoop()
  1391. #         self.assert_(self.callbackCalled)
  1392. #         self.assertEquals(self.data['status'], 200)
  1393.  
  1394. #     def testGrabURL(self):
  1395. #         httpclient.grabURL('https://www.gibill.va.gov/wave/', self.callback,
  1396. #                 self.errback, clientClass=TestHTTPClient)
  1397. #         self.runEventLoop()
  1398. #         self.assert_(self.callbackCalled)
  1399. #         self.assertEquals(self.data['status'], 200)
  1400.  
  1401. class GrabURLTest(AsyncSocketTest):
  1402.     def testStart(self):
  1403.         url = 'http://participatoryculture.org/democracytest/normalpage.txt'
  1404.         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient)
  1405.         self.runEventLoop()
  1406.         self.origData = self.data
  1407.         httpclient.grabURL(url, self.callback, self.errback, start=4, clientClass=TestHTTPClient)
  1408.         self.runEventLoop()
  1409.         self.assertEquals(self.data['body'], self.origData['body'])
  1410.         self.assertEquals(self.data['status'], 200)
  1411.  
  1412. #     def testEtag(self):
  1413. #         url = 'http://jigsaw.w3.org/HTTP/'
  1414. #         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient)
  1415. #         self.runEventLoop()
  1416. #         etag = self.data['etag']
  1417. #         httpclient.grabURL(url, self.callback, self.errback, etag=etag, clientClass=TestHTTPClient)
  1418. #         self.runEventLoop()
  1419. #         self.assertEquals(self.data['status'], 304)
  1420. #         self.assertEquals(self.data['body'], '')
  1421.  
  1422. #     def testBadEtag(self):
  1423. #         url = 'http://jigsaw.w3.org/HTTP/'
  1424. #         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient)
  1425. #         self.runEventLoop()
  1426. #         etag = "aaaaaaa:bbbbbbbb"
  1427. #         firstBody = self.data['body']
  1428. #         httpclient.grabURL(url, self.callback, self.errback, etag=etag, clientClass=TestHTTPClient)
  1429. #         self.runEventLoop()
  1430. #         self.assertEquals(self.data['status'], 200)
  1431. #         self.assertEquals(self.data['body'], firstBody)
  1432.  
  1433. #     def testModified(self):
  1434. #         url = 'http://jigsaw.w3.org/HTTP/'
  1435. #         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient)
  1436. #         self.runEventLoop()
  1437. #         firstBody = self.data['body']
  1438. #         modifiedTuple = rfc822.parsedate_tz(self.data['last-modified'])
  1439. #         modifiedTime = rfc822.mktime_tz(modifiedTuple)
  1440. #         modifiedTime -= 5
  1441. #         httpclient.grabURL(url, self.callback, self.errback,
  1442. #                 modified=rfc822.formatdate(modifiedTime, clientClass=TestHTTPClient))
  1443. #         self.runEventLoop()
  1444. #         self.assertEquals(self.data['status'], 200)
  1445. #         self.assertEquals(self.data['body'], firstBody)
  1446.  
  1447.  
  1448. # class HTTPClientPipelineCounter(httpclient.HTTPClient):
  1449. #     def __init__(self, url, callback, errback):
  1450. #         httpclient.HTTPClient.__init__(self, url, callback, errback)
  1451. #         self.pipelineErrorsSeen = 0
  1452.  
  1453. #     def errbackIntercept(self, error):
  1454. #         if isinstance(error, httpclient.PipelinedRequestNeverStarted):
  1455. #             self.pipelineErrorsSeen += 1
  1456. #         return httpclient.HTTPClient.errbackIntercept(self, error)
  1457.  
  1458. # class PipelineTest(HTTPClientTestBase):
  1459. #     def setUp(self):
  1460. #         HTTPClientTestBase.setUp(self)
  1461. #         self.pool = TestingHTTPConnectionPool()
  1462. #         self.pool.MAX_CONNECTIONS_PER_SERVER = 1
  1463. #         url = "http://www.foo.com/"
  1464. #         self.firstClient = httpclient.HTTPClient(url, self.callback,
  1465. #                 self.errback) 
  1466. #         self.firstClient.connectionPool = self.pool
  1467. #         self.firstClient.startRequest()
  1468. #         url = "http://www.foo.com/2"
  1469. #         self.pipelineResponse = self.pipelineError = None
  1470. #         def pipelineCallback(response):
  1471. #             self.pipelineResponse = response
  1472. #         def pipelineErrback(error):
  1473. #             self.pipelineError = error
  1474. #         self.runPendingIdles()
  1475. #         conn = self.pool.getConnection('http', 'www.foo.com')
  1476. #         conn.handleData(startResponse(headers={'Content-Length': 128}))
  1477. #         self.pipelinedClient = HTTPClientPipelineCounter(url,
  1478. #                 pipelineCallback, pipelineErrback) 
  1479. #         self.pipelinedClient.connectionPool = self.pool
  1480. #         self.pipelinedClient.startRequest()
  1481. #         self.runPendingIdles()
  1482.  
  1483. #     def testPipelineRetry(self):
  1484. #         conn = self.pool.getConnection('http', 'www.foo.com')
  1485. #         self.assertEquals(self.firstClient.connection, conn)
  1486. #         self.assertEquals(self.pipelinedClient.connection, None)
  1487. #         conn.closeConnection()
  1488. #         conn.handleClose(socket.SHUT_RD)
  1489. #         self.runPendingIdles()
  1490. #         self.assertEquals(self.pipelineError, None)
  1491. #         self.assertEquals(self.pipelineResponse, None)
  1492. #         conn = self.pool.getConnection('http', 'www.foo.com')
  1493. #         conn.handleData(HTTPClientTest.fakeResponse)
  1494. #         self.assertEquals(self.pipelineResponse['body'], "HELLO: WORLD\r\n")
  1495. #         self.assertEquals(self.pipelinedClient.pipelineErrorsSeen, 1)
  1496.  
  1497. #     def testPipelineCancel(self):
  1498. #         # canceling the pipelined request shouldn't affect the earlier one.
  1499. #         self.pipelinedClient.cancel()
  1500. #         conn = self.pool.getConnection('http', 'www.foo.com')
  1501. #         self.assert_(conn is not None)
  1502. #         conn.handleData('a' * 128)
  1503. #         conn = self.pool.getConnection('http', 'www.foo.com', 'free')
  1504. #         self.assert_(conn is None)
  1505.  
  1506. #     def testPipelineCancel2(self):
  1507. #         # canceling the earlier request should result in the pipeline request
  1508. #         # retrying
  1509. #         self.firstClient.cancel()
  1510. #         self.runPendingIdles()
  1511. #         self.assertEquals(self.pipelineError, None)
  1512. #         self.assertEquals(self.pipelineResponse, None)
  1513. #         conn = self.pool.getConnection('http', 'www.foo.com')
  1514. #         conn.handleData(HTTPClientTest.fakeResponse)
  1515. #         self.assertEquals(self.pipelineResponse['body'], "HELLO: WORLD\r\n")
  1516.  
  1517. #     def checkPipelineCanceled(self):
  1518. #         self.assertEquals(self.pipelineError, None)
  1519. #         self.assertEquals(self.pipelineResponse, None)
  1520. #         conn = self.pool.getConnection('http', 'www.foo.com', type='active')
  1521. #         self.assert_(conn is None)
  1522. #         conn = self.pool.getConnection('http', 'www.foo.com', type='free')
  1523. #         self.assert_(conn is None)
  1524.  
  1525. #     def testPipelineCancel3(self):
  1526. #         # If we cancel the pipeline, then canceling the earlier request
  1527. #         self.pipelinedClient.cancel()
  1528. #         self.firstClient.cancel()
  1529. #         self.runPendingIdles()
  1530. #         self.checkPipelineCanceled()
  1531.  
  1532. #     def testPipelineCancel4(self):
  1533. #         # test canceling the pipeline, then letting the 1st request finish
  1534. #         self.pipelinedClient.cancel()
  1535. #         conn = self.pool.getConnection('http', 'www.foo.com')
  1536. #         conn.handleData(HTTPClientTest.fakeResponse)
  1537. #         self.runPendingIdles()
  1538. #         self.checkPipelineCanceled()
  1539.  
  1540. class BadURLTest(HTTPClientTestBase):
  1541.     def testScheme(self):
  1542.         url = 'participatoryculture.org/democracytest/normalpage.txt'
  1543.         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient)
  1544.         self.runPendingIdles()
  1545.         self.assertEquals(self.errbackCalled, True)
  1546.         self.assertEquals(self.callbackCalled, False)
  1547.  
  1548.     def testSlashes(self):
  1549.         url = 'http:jigsaw.w3.org/HTTP/'
  1550.         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient)
  1551.         self.runPendingIdles()
  1552.         self.assertEquals(self.errbackCalled, True)
  1553.         self.assertEquals(self.callbackCalled, False)
  1554.  
  1555.     def testHost(self):
  1556.         url = 'http:///HTTP/'
  1557.         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient)
  1558.         self.runPendingIdles()
  1559.         self.assertEquals(self.errbackCalled, True)
  1560.         self.assertEquals(self.callbackCalled, False)
  1561.  
  1562.     def testOtherScheme(self):
  1563.         url = 'rtsp://jigsaw.w3.org/'
  1564.         httpclient.grabURL(url, self.callback, self.errback, clientClass=TestHTTPClient)
  1565.         self.runPendingIdles()
  1566.         self.assertEquals(self.errbackCalled, True)
  1567.         self.assertEquals(self.callbackCalled, False)
  1568.  
  1569. # class SocketCallbackTest(EventLoopTest):
  1570. #     """This is a really weird situation, we"""
  1571.  
  1572. #     def setUp(self):
  1573. #         EventLoopTest.setUp(self)
  1574.  
  1575. #     def makeSocket(self):
  1576. #         sock = socket.socket()
  1577. #         sock.connect(('participatoryculture.org', 80))
  1578. #         return sock
  1579.  
  1580. #     def testAddOnExisting(self):
  1581. #         # This test fails right now..  It seems like it could be an error, but
  1582. #         # I'm don't think it causes any harm, so I'm disabling it - Ben
  1583. #         return
  1584.  
  1585. #         s1 = self.makeSocket()
  1586. #         s2 = self.makeSocket()
  1587. #         self.badCallbackCalled = self.goodCallbackCalled = False
  1588. #         def badCallback():
  1589. #             self.badCallbackCalled = True
  1590. #         def goodCallback():
  1591. #             self.goodCallbackCalled = True
  1592. #         def callback():
  1593. #             self.addWriteCallback(s2, badCallback)
  1594. #         self.addWriteCallback(s1, callback)
  1595. #         self.addWriteCallback(s2, goodCallback)
  1596. #         self.assert_(self.goodCallbackCalled)
  1597. #         self.assert_(not self.badCallbackCalled)
  1598.  
  1599. #     def testRemoveThenAdd(self):
  1600. #         """Test adding a write callback right after we remove one.  This case
  1601. #         isn't so bad actually, the really bad case (which I don't know how to
  1602. #         simulate, is if we closed s2, then made a new socket, s3 that had the
  1603. #         same fileno as s2 used to have."""
  1604. #         s1 = self.makeSocket()
  1605. #         s2 = self.makeSocket()
  1606. #         self.count = 0
  1607. #         def callback():
  1608. #             self.count += 1
  1609. #             if self.count == 1:
  1610. #                 self.removeWriteCallback(s1)
  1611. #                 self.addWriteCallback(s1, callback)
  1612. #                 self.removeWriteCallback(s2)
  1613. #                 self.addWriteCallback(s2, callback)
  1614. #         self.addWriteCallback(s1, callback)
  1615. #         self.addWriteCallback(s2, callback)
  1616. #         self.addIdle(lambda: self.stopEventLoop(False), 'stop event loop')
  1617. #         self.runEventLoop()
  1618. #         self.assertEquals(self.count, 1)
  1619.  
  1620.  
  1621. class CookieExpirationDateTestCase(unittest.TestCase):
  1622.     def testCookieExpirationDate(self):
  1623.         """Tests get_cookie_expiration_date to make sure it's returning
  1624.         sane values and handles cookie expiration formats we've had
  1625.         problems with.
  1626.         """
  1627.         from time import mktime, strptime, localtime
  1628.         from httpclient import get_cookie_expiration_date
  1629.         
  1630.         for cd in ( ("Thu, 03-May-07 22:48:52 GMT", "2007-05-03 22:48:52 GMT" ),
  1631.                     ("Fri, 03-Jun-11 13:41:15 GMT", "2011-06-03 13:41:15 GMT" ),
  1632.                     ("Sun, 17-Jan-2038 19:14:07 GMT", "2038-01-17 19:14:07 GMT" ),
  1633.                     ("Mon, 09-Apr-07 23:50:49 GMT", "2007-04-09 23:50:49 GMT" ),
  1634.                     ("Tue, 01-Jan-2030 10:00:00 GMT", "2030-01-01 10:00:00 GMT" ),
  1635.                     ("Tue, 17-Jul-2007 02:09:00 GMT", "2007-07-17 02:09:00 GMT") ):
  1636.             
  1637.             # compare the 9-tuple we get from localtime because tuples are
  1638.             # easier to compare (and more accurate for what we're looking for)
  1639.             self.assertEquals( localtime(get_cookie_expiration_date(cd[0])),
  1640.                                localtime(mktime(strptime(cd[1], "%Y-%m-%d %H:%M:%S %Z"))) )
  1641.             
  1642.     def testOverflowCookieExpirationDate(self):
  1643.         """tests the case of get_cookie_expiration_date where the cookie
  1644.         expiration date causes an overflow error when parsing it.
  1645.         """
  1646.         from time import localtime
  1647.         from httpclient import get_cookie_expiration_date, DATEINFUTURE
  1648.             
  1649.         self.assertEquals( localtime(get_cookie_expiration_date("Tue, 26-Jul-2050 10:00:00 GMT")),
  1650.                            localtime(httpclient.DATEINFUTURE) )
  1651.